package com.wu.util;

import com.wu.annotation.WebServlet;
import com.wu.constant.Constant;

import java.io.File;
import java.util.*;

/**
 * 包扫描工具类
 *
 * @author 吴磊
 */
public class PackageScan {

    private PackageScan PackageScan() {
        return new PackageScan();
    }

    /**
     * 存放所有被扫描到的class类
     */
    private static final List<String> CLASS_PATHS = new ArrayList<>();

    /**
     * 存放所有带注解WebServlet的类名
     */
    private static final List<String> CLASS_NAME_LIST = new ArrayList<>();

    /**
     * 存放所有带WebServlet注解的  类路径 - 路径   Map键值对
     */
    private static final Map<String, List<String>> REQUEST_PATHS = new HashMap<>();

    /**
     * 获取app包下所有的请求路径
     * 只有加了WebServlet注解的类才会添加到
     */
    public static Map<String, List<String>> getRequestPaths() {
        return REQUEST_PATHS;
    }

    /**
     * 存放所有带注解WebServlet的类名
     */
    public static List<String> getClassNameList() {
        return CLASS_NAME_LIST;
    }

    /**
     * 扫描 basePackage 下全部带 {@link WebServlet} 的类
     *
     * @param basePackage Java类存放的包名 例如 ：com.wu.app
     */
    public static void searchClass(String basePackage) {
        //先把包名转换为路径,首先得到项目的classpath
        String classPath = Objects.requireNonNull(TomcatUtil.class.getResource("/")).getPath();
        //然后把我们的包名basePack转换为路径名
        basePackage = basePackage.replace(".", File.separator);
        //然后把classPath和basePack合并
        String searchPath = classPath + basePackage;
        getAllClassPath(new File(searchPath));
        //临时存放请求路径，防止请求路径重复
        Set<String> pathSet = new HashSet<>();
        //这个时候我们已经得到了指定包下所有的类的绝对路径了。我们现在利用这些绝对路径和java的反射机制得到他们的类对象
        for (String path : CLASS_PATHS) {
            //把 D:\tomcatServer\target\classes\com\wu\HelloServlet.class 这样的绝对路径转换为全类名com.wu.search.HelloServlet
            if (System.getenv("OS").toUpperCase().indexOf("WIN") == 0) {
                path = path.replace(classPath.replace("/", "\\").replaceFirst("\\\\", ""), "").replace("\\", ".").replace(Constant.CLASS_SUFFIX, "");
            } else {
                path = path.replace(classPath, "").replace(Constant.CLASS_SUFFIX, "").replace("/", ".").replace("\\", ".");
            }
            getAnnotationClass(pathSet, path);
        }
    }

    /**
     * 获取带有 {@link WebServlet} 的类
     *
     * @param classPath 类的全限类路径
     */
    private static void getAnnotationClass(Set<String> pathSet, String classPath) {
        Class<?> cls = null;
        try {
            cls = Class.forName(classPath);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        assert cls != null;
        WebServlet annotation = cls.getAnnotation(WebServlet.class);
        if (annotation != null && annotation.value().length > 0) {
            CLASS_NAME_LIST.add(cls.getName());
            String[] values = annotation.value();
            for (String value : values) {
                if (pathSet.contains(value)) {
                    try {
                        throw new Exception("The request path has been repeated：" + value);
                    } catch (Exception e) {
                        e.printStackTrace();
                        System.exit(-1);
                    }
                }
                pathSet.add(value);
            }
            ArrayList<String> valuesList = new ArrayList<>(Arrays.asList(values));
            REQUEST_PATHS.put(cls.getName(), valuesList);
        }
    }


    /**
     * 该方法会得到所有的类，将类的绝对路径写入到 CLASS_PATHS 中
     *
     * @param file 文件
     */
    private static void getAllClassPath(File file) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            assert files != null;
            for (File f : files) {
                getAllClassPath(f);
            }
        } else {
            if (file.getName().endsWith(Constant.CLASS_SUFFIX)) {
                CLASS_PATHS.add(file.getPath());
            }
        }
    }
}
